In [1]:
/*
Data Type(자료형식)을 도입할 때는 trait(특질) 키워드를 사용
trait는 일종의 추상 인터페이스, 필요하면 메서드도 구현할 수 있음.
sealed 키워드는 이 trait의 모든 구현이 반드시 이 파일 안에 정의되어 있어야함을 뜻함.
*/
// Singly linked list
sealed trait List[+A] // 형식 A에 대해 매개변수화된 List자료 형식,
// A앞에 있는 '+'는 가변 지정자(variance annotaion). '+'가 붙으면 공변(covariant)
// '+'가 붙어 있지 않으면 불변(invariant)
case object Nil extends List[Nothing] // 빈 리스트를 나타내는 List 자료 생성자
case class Cons[+A](head: A, tail: List[A]) extends List[A] // 비지 않은 리스트를 나타내는 List 자료 생성자. tail은 또 다른 Cons or Nil
// List의 Companion object (trait와 이름이 같은 객체)
// List의 생성과 조작을 위한 함수들을 담는다.
object List {
def sum(ints: List[Int]): Int = ints match { // 패턴 매치를 이용해서 정수 List의 합을 구하는 함수
case Nil => 0 // 빈 리스트 Nil의 경우 0을 리턴
case Cons(x, xs) => x + sum(xs) // head가 x인 리스트는 x 더하기 tail의 합
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x, xs) => x * product(xs)
}
def apply[A](as: A*): List[A] = // A* 는 가변 인수를 나타냄 타입이 A인 인수를 여러개 받음 ','로 구분
if(as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
Out[1]:
In [2]:
List(1,2,3,4,5)
Out[2]:
In [3]:
List.sum(res1)
Out[3]:
In [4]:
val ex1: List[Double] = Nil
val ex2: List[Int] = Cons(1, Nil)
val ex3: List[String] = Cons("a", Cons("b", Nil))
Out[4]:
target과 일치하는 패턴에 해당하는 결과를 리턴하는 구문. 위에 있는 패턴부터 검증, 여러 패턴과 일치하는 경우 위에서 부터 검증하기 때문에 최상위에 있는 패턴의 결과를 리턴함.
target match {
case pattern1 => result
case pattern2 => result
...
}
패턴은 3
이나 "hi"
같은 리터럴과 x
나 xs
같이 소문자나 밑줄로 시작하는 식별자로 된 변수, 그리고 Cons(x, xs)
나 Nil
같은 자료 생성자로 구성됨. 변수는 모든 것에 부합하는 반면 자료 생성자는 해당 형태의 값에만 부합.
In [5]:
// Pattern Matching Example
List(1,2,3) match { case _ => 42 }
List(1,2,3) match { case Cons(h, _) => h }
List(1,2,3) match { case Cons(_, t) => t }
// List(1,2,3) match { case Nil => 42}
Out[5]:
In [6]:
def tail[A](as: List[A]): List[A] = as match {
case Nil => throw new UnsupportedOperationException
case Cons(_, t) => t
}
Out[6]:
In [7]:
tail(List(1,2,3,4))
Out[7]:
In [8]:
def setHead[A](l: List[A], x: A): List[A] = l match {
case Nil => throw new UnsupportedOperationException
case Cons(h, t) => Cons(x, t)
}
Out[8]:
In [9]:
setHead(List(1,2,3), 8)
Out[9]:
In [10]:
def drop[A](l: List[A], n: Int): List[A] = {
def loop[A](as: List[A], n: Int): List[A] =
if(n>0) loop(tail(as), n-1)
else as
loop(l, n)
}
Out[10]:
In [11]:
drop(List(1,2,3,4,5), 2)
Out[11]:
In [12]:
def dropWhile[A](l: List[A], f: A => Boolean): List[A] = l match {
case Nil => Nil
case Cons(h, t) if(f(h)) => dropWhile(t, f)
case _ => l
}
Out[12]:
In [13]:
dropWhile(List(1,2,3,4,5), ((x: Int) => x % 2 == 0))
Out[13]:
In [14]:
def init[A](l: List[A]): List[A] = l match {
case Cons(_, Nil) => Nil
case Cons(h, t) => Cons(h, init(t))
}
// 리스트의 첫 원소인 head는 바로 접근이 가능하지만
// tail은 앞에서 부터 차례대로 접근해야하기 때문에 리스트의 길이만큼의 시간이 걸림
Out[14]:
In [15]:
init(List(1,2,3,4))
Out[15]:
def sum(ints: List[Int]): Int = ints match {
case Nil => 0 // 빈 리스트 Nil의 경우 0을 리턴
case Cons(x, xs) => x + sum(xs) // head가 x인 리스트는 x 더하기 tail의 합
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0 // 빈 리스트 Nil의 경우 1.0을 리턴
case Cons(x, xs) => x * product(xs) // head가 x인 리스트는 x 곱하기 tail의 곱
}
위와 같이 리스트를 다루는 함수는 비슷한 형태를 취하고 있음 이를 기반으로 함수를 일반화함
In [16]:
// 재귀 호출 형태의 함수들을 일반화한 foldRight 함수
def foldRight[A,B](as: List[A], z: B)(f: (A, B) => B): B = as match {
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
// foldRight를 활용해서 재구현한 sum 함수와 product 함수
def sum2(ns: List[Int]): Int =
foldRight(ns, 0)((x, y) => x + y)
def product2(ns: List[Double]): Double =
foldRight(ns, 1.0)(_ * _)
Out[16]:
In [17]:
val intList = List(1,2,3)
sum2(intList)
Out[17]:
위 sum2의 호출 구조를 트레이스 해보면 다음과 같음
foldRight(Cons(1, Cons(2, Cons(3, Nil))), 0)((x,y) => x + y)
1 + foldRight(Cons(2, Cons(3, Nil)), 0)((x,y) => x + y)
1 + (2 + foldRight(Cons(3, Nil), 0)((x,y) => x + y))
1 + (2 + (3 + (foldRight(Nil, 0)((x,y) => x + y))))
1 + (2 + (3 + (0)))
6
In [18]:
def length[A](as: List[A]): Int =
foldRight(as, 0)((_, x) => x + 1)
Out[18]:
In [19]:
length(List(1,2,3,4,5))
Out[19]:
In [20]:
def foldLeft[A,B](as: List[A], z: B)(f: (B, A) => B): B = as match {
case Nil => z
case Cons(x, xs) => foldLeft(xs, f(z, x))(f)
}
Out[20]:
In [21]:
def sumViaFL(as: List[Int]): Int =
foldLeft(as, 0)((x, a) => x + a)
def prodViaFL(as: List[Double]): Double =
foldLeft(as, 1.0)((x, a) => x * a)
def lengthViaFL[A](as: List[A]): Int =
foldLeft(as, 0)((x, _) => x + 1)
Out[21]:
In [22]:
def reverse[A](as: List[A]): List[A] =
foldLeft(as, Nil: List[A])((z, x) => Cons(x, z))
Out[22]:
In [23]:
def foldLeft2[A,B](as: List[A], z: B)(f: (B, A) => B): B =
foldRight(as, (b:B) => b)((a, g) => b => g(f(b,a)))(z)
def foldRight2[A,B](as: List[A], z:B)(f: (A, B) => B): B =
foldLeft(as, (b:B) => b)((g, a) => b => g(f(a,b)))(z)
Out[23]:
In [24]:
def append[A](a1: List[A], a2: List[A]): List[A] =
foldRight(a1, a2)(Cons(_, _))
Out[24]:
In [25]:
def concat[A](aa: List[List[A]]): List[A] =
foldRight(aa, Nil: List[A])(append(_, _))
Out[25]:
In [25]:
foldRight(aa, Nil: List[A])(List(1,2,3,4)))
In [26]:
def inc(ai: List[Int]): List[Int] =
foldRight(ai, Nil: List[Int])((x, z) => Cons(x + 1 , z))
Out[26]:
In [27]:
inc(List(1,2,3))
Out[27]:
In [28]:
def incN(ai: List[Int], n: Int): List[Int] =
foldRight(ai, Nil: List[Int])((x, z) => Cons(x + n , z))
Out[28]:
In [29]:
incN(List(1,2,3), 3)
Out[29]:
In [30]:
def doubleToString(ds: List[Double]): List[String] =
foldRight(ds, Nil: List[String])((h, t) => Cons(h.toString, t))
Out[30]:
In [31]:
doubleToString(List(1.2, 3.4))
Out[31]:
In [32]:
// def foldRight[A,B](as: List[A], z: B)(f: (A, B) => B): B
// def inc(as: List[Int]): List[Int] =
// foldRight(as, Nil: List[Int])((h, t) => Cons(h + 1 , t))
// def doubleToString(as: List[Double]): List[String] =
// foldRight(as, Nil: List[String])((h, t) => Cons(h.toString, t))
def map[A,B](as: List[A])(f: A => B): List[B] =
foldRight(as, Nil: List[B])((h, t) => Cons(f(h), t))
Out[32]:
In [33]:
def filter[A](as: List[A])(f: A => Boolean): List[A] =
foldRight(as, Nil: List[A])((h, t) => if(f(h)) Cons(h, t) else t)
Out[33]:
In [34]:
filter(List(1,2,3,4))(x => x % 2 == 0)
Out[34]:
In [35]:
def flatMap[A,B](as: List[A])(f: A => List[B]): List[B] =
foldRight(as, Nil: List[B])((h, t) => append(f(h), t))
def flatMap_[A,B](as: List[A])(f: A => List[B]): List[B] =
concat(map(as)(f))
Out[35]:
In [36]:
map(List(1,2,3))(i => List(i,i))
Out[36]:
In [37]:
def filter[A](as: List[A])(f: A => Boolean): List[A] =
flatMap(as)(a => if(f(a)) Cons(a, Nil) else Nil)
Out[37]:
In [38]:
filter(List(1,2,3,4))(x => x % 2 == 0)
Out[38]:
In [ ]: